home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / web / knit / knit.ch < prev    next >
Text File  |  1992-09-23  |  34KB  |  910 lines

  1. % This is knit.ch
  2. This is a changefile for TANGLE which produces KNIT, a version of TANGLE
  3. which allows multiple changefiles (see TUGBoat Vol.7, No. 1, pg. 20-21).
  4. @x We modify the text
  5. @* Introduction.
  6. This program converts a \.{WEB} file to a \PASCAL\ file. It was written
  7. by D. E. Knuth in September, 1981; a somewhat similar {\mc SAIL} program had
  8. been developed in March, 1979. Since this program describes itself, a
  9. bootstrapping process involving hand-translation had to be used to get started.
  10. @y
  11. \def\title{KNIT}
  12. \def\contentspagenumber{1} % should be odd
  13. \def\topofcontents{\null\vfill
  14.   \titlefalse % include headline on the contents page
  15.   \def\rheader{\mainfont Appendix E\hfil \contentspagenumber}
  16.   \centerline{\titlefont The {\ttitlefont KNIT} processor}
  17.   \vskip 15pt
  18.   \centerline{(Version 2.6)}
  19.   \vfill}
  20. \pageno=\contentspagenumber \advance\pageno by 1
  21. @* Introduction.
  22. This program converts a \.{WEB} file to a \PASCAL\ file.  Several
  23. changes have been made by GMD, to allow \.{TANGLE} to handle more
  24. than one |change_file|. It is basically the original \.{TANGLE} written by
  25. D.E. Knuth, but we'll name this \.{TANGLE}--Version \.{KNIT} to distinguish it
  26. from the original one. The program was created by writing a |change_file|
  27. modifying |tangle.web|. To keep that |change_file| as small as possible we
  28. didn't change the name ``\.{TANGLE}'' througout the documentation.
  29.  
  30. \bigskip
  31. If you find a bug in the program you should send a message to:
  32.  
  33. {\obeylines\parskip=0pt
  34.       W.Appelt, K.Horn
  35.       GMD Gesellschaft f\"ur Mathematik und Datenverarbeitung
  36.       Schlo\ss\ Birlinghoven
  37.       Postfach 1240
  38.       D-5205 Sankt Augustin
  39.  
  40.       West-Germany
  41.  
  42. }
  43. \bigskip
  44.  
  45. The original version was written
  46. by D. E. Knuth in September, 1981; a somewhat similar {\mc SAIL} program had
  47. been developed in March, 1979. Since this program describes itself, a
  48. bootstrapping process involving hand-translation had to be used to get started.
  49. @z
  50.  
  51. @x (1) we modify the banner line
  52. The ``banner line'' defined here should be changed whenever \.{TANGLE}
  53. is modified.
  54.  
  55. @d banner=='This is TANGLE, Version 2.6'
  56. @y
  57. The ``banner line'' defined here should be changed whenever \.{KNIT}
  58. is modified.
  59. @d banner=='This is KNIT based on TANGLE, Version 2.6, knitted by GMD'
  60. @d term_in==input
  61. @d term_out==output
  62. @z
  63.  
  64. @x (2) Now we allow three change-files
  65. @ The program begins with a fairly normal header, made up of pieces that
  66. @^system dependencies@>
  67. will mostly be filled in later. The \.{WEB} input comes from files |web_file|
  68. and |change_file|, the \PASCAL\ output goes to file |Pascal_file|,
  69. and the string pool output goes to file |pool|.
  70. @y
  71. @ Changes have been made to allow more than one |change_file|.
  72. In this \.{KNIT}--version three |change_files| are used. The necessary new
  73. definitions are inserted quite below this explanation, so don't be
  74. astonished to find a macro-definition in this module.
  75. Changing the number of |change_files| only requires updates in this
  76. module.
  77.  
  78. The |change_files| have descending priorities. That means, if two
  79. |change_files| want to modify the same line in a |web_file| , there will be a
  80. warning and  the |change_file| with the lower priority will be advanced
  81. to the next section without influencing the input. If a |change_file| wants
  82. to modify lines within the |web_file_| which are currently already changed
  83. by another |change_file| these changes are ignored.
  84. A warning will be generated.
  85.  
  86. First you should examine this little example of \.{KNIT} with three
  87. |change_files|:
  88.  
  89. \bigskip
  90. Input files:
  91. $$\vbox{{\tt
  92. \halign{&
  93. \quad#\hfill\quad&\quad#\hfill\quad&\quad#\hfill\quad&\quad#\hfill\quad\cr
  94. |web_file|  &   |ch_file_1|  &  |ch_file_2|  &  |ch_file_3|     \cr
  95. line1       &   @@x          &  @@x          &  @@x             \cr
  96. line2       &   line1        &  line5        &  line1           \cr
  97. line3       &   @@y          &  @@y          &  line2           \cr
  98. line4       &   line1from1   &  line5from2   &  @@y             \cr
  99. line5       &   line2from1   &  @@z          &  line1from3      \cr
  100. line6       &   line3from1   &  @@x          &  @@z             \cr
  101. line7       &   @@z          &  line10       &  @@x             \cr
  102. line8       &   *EOF*        &  @@y          &  line4           \cr
  103. line9       &                &  line10from2  &  line5           \cr
  104. line10      &                &  line11from2  &  line6           \cr
  105. line11      &                &  @@z          &  @@y             \cr
  106. line12      &                &  *EOF*        &  line4from3      \cr
  107. *EOF*       &                &               &  line5from3      \cr
  108.         &                &               &  @@z             \cr
  109.         &                &               &  *EOF*           \cr
  110. }}
  111. }
  112. $$
  113.  
  114. The result will be that the program will process the following lines for
  115. output:
  116.  
  117. {\tt\obeylines
  118. ***WARNING...     (ch\_file\_3 will be advanced)
  119. line1from1
  120. line2from1
  121. line3from1
  122. line2
  123. line3
  124. ***WARNING...     (ch\_file\_2 will be advanced)
  125. line4from3
  126. line5from3
  127. line7
  128. line8
  129. line9
  130. line10from2
  131. line11from2
  132. line11
  133. line12
  134. *EOF*
  135. }
  136.  
  137. Now we want to explain the macros defined in this module. They serve to
  138. avoid arrays of files since some PASCAL-compilers can't
  139. handle arrays of files. Behind this we find the main-program's skeleton.
  140.  
  141. The program begins with a fairly normal header, made up of pieces that
  142. @^system dependencies@>
  143. will mostly be filled in later. The \.{WEB} input comes from |web_file|
  144. and the |change_files|, the \PASCAL\ output goes to file |pascal_file|,
  145. and the string pool output goes to file |pool|.
  146. @z
  147.  
  148. @x we have to split the change file to allow system-dependent changes
  149. @d end_of_TANGLE = 9999 {go here to wrap it up}
  150. @y
  151. @d end_of_TANGLE = 9999 {go here to wrap it up}
  152.  
  153. @d ch_max==3  {indicates the maximum number of |change_files| with descending
  154. priority. If this number is modified the following three definitions and the
  155. program statement must be updated as well}
  156.  
  157. @d input_ch(#) == case # of
  158.           1: ch_not_eof:=input_ln(ch_file_1);
  159.           2: ch_not_eof:=input_ln(ch_file_2);
  160.           3: ch_not_eof:=input_ln(ch_file_3);end
  161.  
  162. @d reset_ch(#) == case # of
  163.           1: reset(ch_file_1);
  164.           2: reset(ch_file_2);
  165.           3: reset(ch_file_3);end
  166.  
  167. @d declaration_ch_files ==@/
  168.             @! ch_file_1:text_file;
  169.             @! ch_file_2:text_file;
  170.             @! ch_file_3:text_file
  171. @z
  172.  
  173. @x (2) we modify the program header and define term_in and term_out
  174. program TANGLE(@!web_file,@!change_file,@!Pascal_file,@!pool);
  175. @y
  176. program KNIT(@!term_in,@!term_out,@!web_file,@!ch_file_1,@!ch_file_2,
  177.            @!ch_file_3,@!Pascal_file,@!pool);
  178. @z
  179. @x Default in case statement is otherwise. (7)
  180. @d othercases == others: {default for cases not listed explicitly}
  181. @y
  182. @d othercases == otherwise {default for cases not listed explicitly}
  183. @z
  184. @x term_out (output) must not be redefined. (20)
  185. @!term_out:text_file; {the terminal as an output file}
  186. @y
  187. @z
  188.  
  189. @x term_out (output) must not be opened. (21)
  190. rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
  191. @y
  192. @z
  193.  
  194. @x break is replaced by writeln (22)
  195. @d update_terminal == break(term_out) {empty the terminal output buffer}
  196. @y
  197. @d update_terminal == write_ln(term_out) {empty the terminal output buffer}
  198. @z
  199.  
  200. @x (23) we declare the maximum number (ch_max) of change-files
  201. @ The main input comes from |web_file|; this input may be overridden
  202. by changes in |change_file|. (If |change_file| is empty, there are no changes.)
  203.  
  204. @<Globals...@>=
  205. @!web_file:text_file; {primary input}
  206. @!change_file:text_file; {updates}
  207. @y
  208. @ The main input comes from |web_file|; this input may be overridden
  209. by changes in the |change_files|. The |change_files| have descending
  210. priority. Only one change may be done to the same text in the |web_file|.
  211. (If all |change_files| are empty, there are no changes.)
  212. Whenever the number of |change_files| is increased the correspondent
  213. file-declarations have to be modified. Since we have written a macro
  214. for this in module~2 of \.{KNIT}, no more changes are necessary here.
  215.  
  216. @<Globals...@>=
  217. @!web_file:text_file; {primary input}
  218. declaration_ch_files; {look for the macro definitions at the beginning}
  219. @z
  220.  
  221. @x (24) we have to reset all change-files explicitely
  222. @ The following code opens the input files.  Since these files were listed
  223. in the program header, we assume that the \PASCAL\ runtime system has
  224. already checked that suitable file names have been given; therefore no
  225. additional error checking needs to be done.
  226. @^system dependencies@>
  227.  
  228. @p procedure open_input; {prepare to read |web_file| and |change_file|}
  229. begin reset(web_file); reset(change_file);
  230. end;
  231. @y
  232. @ The following code opens the input files.  Since these files were listed
  233. in the program header, we assume that the \PASCAL\ runtime system has
  234. already checked that suitable file names have been given; therefore no
  235. additional error checking needs to be done.
  236. @^system dependencies@>
  237. The macro |reset_ch(i)| resets the |i-th change_file|.
  238. This is done by a macro,
  239. so changing the number of |change_files| only makes necessary a modification
  240. in the macro written in module~2 of the \.{KNIT}--program.
  241.  
  242. @p procedure open_input; {prepare to read |web_file| and |change_file|}
  243. var i:integer; {loop variable for processing the |change_files|}
  244. begin reset(web_file); for i:=1 to ch_max do reset_ch(i);end;
  245. @z
  246.  
  247. @x (32) We want to specify the current change-file
  248. @ The error locations during Phase I can be indicated by using the global
  249. variables |loc|, |line|, and |changing|, which tell respectively the first
  250. unlooked-at position in |buffer|, the current line number, and whether or not
  251. the current line is from |change_file| or |web_file|.
  252. This routine should be modified on systems whose standard text editor
  253. has special line-numbering conventions.
  254. @^system dependencies@>
  255.  
  256. @<Print error location based on input buffer@>=
  257. begin if changing then print('. (change file ')@+else print('. (');
  258. print_ln('l.', line:1, ')');
  259. if loc>=limit then l:=limit else l:=loc;
  260. for k:=1 to l do
  261.   if buffer[k-1]=tab_mark then print(' ')
  262.   else print(xchr[buffer[k-1]]); {print the characters already read}
  263. new_line;
  264. for k:=1 to l do print(' '); {space out the next line}
  265. for k:=l+1 to limit do print(xchr[buffer[k-1]]); {print the part not yet read}
  266. print(' '); {this space separates the message from future asterisks}
  267. end
  268. @y
  269. @ The error locations during Phase I can be indicated by using the global
  270. variables |loc|, |line|, and |changing|, which tell respectively the first
  271. unlooked-at position in |buffer|, the current line number, and whether or not
  272. the current line is from  |web_file| or from one of the |change_files|
  273. and if so, from which one. The variable |ch_act| gives the index of
  274. the current |change_file|.
  275. This routine should be modified on systems whose standard text editor
  276. has special line-numbering conventions.
  277. @^system dependencies@>
  278.  
  279. @<Print error location based on input buffer@>=
  280. begin if changing then print('. (change file ',ch_act:1,' ')@+
  281. else print('. (');
  282. print_ln('l.', line:1, ')');
  283. if loc>=limit then l:=limit else l:=loc;
  284. for k:=1 to l do
  285.   if buffer[k-1]=tab_mark then print(' ')
  286.   else print(xchr[buffer[k-1]]); {print the characters already read}
  287. new_line;
  288. for k:=1 to l do print(' '); {space out the next line}
  289. for k:=l+1 to limit do print(xchr[buffer[k-1]]); {print the part not yet read}
  290. print(' '); {this space separates the message from future asterisks}
  291. end
  292. @z
  293. @x No translation to uppercase. (58)
  294.     begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]-@'40
  295. @y
  296.     begin if buffer[i]>="a" then chopped_id[s]:=buffer[i]
  297. @z
  298. @x No translation to uppercase. (63)
  299.     begin if c>="a" then c:=c-@'40; {convert to uppercase}
  300. @y
  301.     begin if c>="a" then c:=c; {no uppercase}
  302. @z
  303. @x No translation to uppercase. (116)
  304. "A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
  305.   end;
  306. "a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
  307.   end;
  308. identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  309.   while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
  310.     begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
  311.     if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
  312.     else if out_contrib[k]="_" then decr(k);
  313.     end;
  314.   send_out(ident,k);
  315.   end;
  316. @y
  317. "A",up_to("Z"): begin out_contrib[1]:=cur_char+@'40; send_out(ident,1);
  318.   end;
  319. "a",up_to("z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
  320.   end;
  321. identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  322.   while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
  323.     begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
  324.     if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]
  325.     else if out_contrib[k]="_" then decr(k);
  326.     end;
  327.   send_out(ident,k);
  328.   end;
  329. @z
  330.  
  331. @x (123) we define a type |ch_type| to characterise the local parameter
  332. @* Introduction to the input phase.
  333. We have now seen that \.{TANGLE} will be able to output the full
  334. \PASCAL\ program, if we can only get that program into the byte memory in
  335. the proper format. The input process is something like the output process
  336. in reverse, since we compress the text as we read it in and we expand it
  337. as we write it out.
  338.  
  339. There are three main input routines. The most interesting is the one that gets
  340. the next token of a \PASCAL\ text; the other two are used to scan rapidly past
  341. \TeX\ text in the \.{WEB} source code. One of the latter routines will jump to
  342. the next token that starts with `\.{@@}', and the other skips to the end
  343. of a \PASCAL\ comment.
  344. @y
  345. @* Introduction to the input phase.
  346. Now we have seen that \.{TANGLE} will be able to output the full
  347. \PASCAL\ program, if we can only get that program into the byte memory in
  348. the proper format. The input process is something like the output process
  349. in reverse, since we compress the text as we read it in and we expand it
  350. as we write it out.
  351.  
  352. There are three main input routines. The most interesting is the one that gets
  353. the next token of a \PASCAL\ text; the other two are used to scan rapidly past
  354. \TeX\ text in the \.{WEB} source code. One of the latter routines will jump to
  355. the next token that starts with `\.{@@}', and the other skips to the end
  356. of a \PASCAL\ comment.
  357.  
  358. In the \.{KNIT}--version of \.{TANGLE} there is more than one |change_file|.
  359. So there are some modifications, which will be explained when they are made.
  360. Altering the number of |change_file| only causes modifications in module~2
  361. of \.{KNIT} since we decided to use macros whereever
  362. the number of |change_files| is important.
  363.  
  364. We need a type |ch_type| to describe the local parameters. This will be done
  365. here.
  366.  
  367. @<Types...@>=
  368. @!ch_type=1..ch_max;
  369. @z
  370.  
  371. @x (126) we expand |change_buffer|, |change_limit| and |ch_line| one dimension
  372. @ When |changing| is |false|, the next line of |change_file| is kept in
  373. |change_buffer[0..change_limit]|, for purposes of comparison with the next
  374. line of |web_file|. After the change file has been completely input, we
  375. set |change_limit:=0|, so that no further matches will be made.
  376.  
  377. @<Globals...@>=
  378. @!change_buffer:array[0..buf_size] of ASCII_code;
  379. @!change_limit:0..buf_size; {the last position occupied in |change_buffer|}
  380. @y
  381. @ Different to the original \.{TANGLE}--version
  382. we are able to handle more than
  383. one |change_file|. So the variables |change_buffer| and
  384. |change_limit| need one more dimension to indicate the current |change_file|.
  385. The index of the current |change_file| will be kept in the variable |ch_act|.
  386. It is set by the function |lines_dont_match|. The variable |ch_not_eof|
  387. takes the value of the |input_ln| function when the input comes from a
  388. |change_file|. To understand why we use this you should look up
  389. the macro definition of |input_ch(#)| in module~2 of \.{KNIT}.
  390. The current line numbers of the |web_file| and all the |change_files|
  391. have to be managed. In the original version this could be done with the
  392. macro |change_changing| defined in the previous module. \.{KNIT} has to handle
  393. more than two files, so we use the array |ch_line| to keep the current line
  394. numbers of all |change_files|. The variable |line| will be updated from
  395. this array each time it is used.
  396. When |changing| is |false|, the next lines of the
  397. |change_files| are kept in their |change_buffers|. The length of the
  398. |change_buffer i| is kept in |change_limit[i]|. After a |change_file|
  399. has been completely input, we set its |change_limit = 0|, so that no
  400. further matches will be made.
  401.  
  402. @<Globals...@>=
  403. @!change_buffer:array[1..ch_max,0..buf_size] of ASCII_code;
  404. @!change_limit:array[1..ch_max] of 0..buf_size;
  405.               {the last position occupied in |change_buffer|}
  406. @!ch_act:1..ch_max; {specifies the current |change_file|}
  407. @!i:1..ch_max;{index used for loops}
  408. @!ch_not_eof:boolean;{corresponds to the boolean |input_ln|}
  409. @!ch_line:array[1..ch_max] of integer; {line refering to all |change_file|s}
  410. @z
  411.  
  412. @x (127) we define an additional function ch_dont_match
  413. @ Here's a simple function that checks if the two buffers are different.
  414.  
  415. @p function lines_dont_match:boolean;
  416. label exit;
  417. var k:0..buf_size; {index into the buffers}
  418. begin lines_dont_match:=true;
  419. if change_limit<>limit then return;
  420. if limit>0 then
  421.   for k:=0 to limit-1 do if change_buffer[k]<>buffer[k] then return;
  422. lines_dont_match:=false;
  423. exit: end;
  424. @y
  425. @ Differing from the original version the index of the current |change_buffer|
  426. has to be specified as a function parameter.
  427. The function |lines_dont_match| checks if |change_buffer[i]| is equal to
  428. the buffer filled by |web_file|. If so, the index of the |change_file|
  429. is copied to the variable |ch_act|. The function |ch_dont_match| checks
  430. if  two  |change_buffers| are equal.
  431.  
  432. @p function lines_dont_match(i:ch_type):boolean;
  433. label exit;
  434. var k:0..buf_size; {index into the buffers}
  435. begin lines_dont_match:=true;
  436. if change_limit[i]<>limit then return;
  437. if limit>0 then
  438.   for k:=0 to limit-1 do if change_buffer[i,k]<>buffer[k] then return;
  439. ch_act:=i;
  440. lines_dont_match:=false;
  441. exit: end;
  442. @#
  443.  
  444. function ch_dont_match(i,j:ch_type):boolean;
  445. label exit;
  446. var k:0..buf_size; {index into the buffers}
  447. begin ch_dont_match:=true;
  448. if ((change_limit[i]=0) or (change_limit[j]=0)) then return;
  449. if change_limit[i]<>change_limit[j] then return;
  450. if change_limit[i]>0 then
  451.    for k:=0 to change_limit[i]-1
  452.        do if change_buffer[i,k]<>change_buffer[j,k] then return;
  453. ch_dont_match:=false;
  454. exit: end;
  455. @z
  456.  
  457. @x (128) now we have to manage more than one change-file
  458. @ Procedure |prime_the_change_buffer| sets |change_buffer| in preparation
  459. for the next matching operation. Since blank lines in the change file are
  460. not used for matching, we have |(change_limit=0)and not changing| if and
  461. only if the change file is exhausted. This procedure is called only
  462. when |changing| is true; hence error messages will be reported correctly.
  463.  
  464. @p procedure prime_the_change_buffer;
  465. label continue, done, exit;
  466. var k:0..buf_size; {index into the buffers}
  467. begin change_limit:=0; {this value will be used if the change file ends}
  468. @<Skip over comment lines in the change file; |return| if end of file@>;
  469. @<Skip to the next nonblank line; |return| if end of file@>;
  470. @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
  471. exit: end;
  472. @y
  473. @ Procedure |prime_the_change_buffer| prepares the addressed
  474. |change_buffer|
  475. for the next matching operation. Since blank lines in the change file are
  476. not used for matching, we have |(change_limit=0)and not changing| if and
  477. only if the change file is exhausted. This procedure is called only
  478. when |changing| is true; hence error messages will be reported correctly.
  479.  
  480. The procedure |pre_prime_the_change_buffer| is necessary if there are two
  481. equal |change_buffers|. The one with the lower priority or the  one which
  482. is actually not compared with the buffer filled by |web_file| will be advanced
  483. to the next matching position behind the @@x-line. First we have to skip over
  484. the @@x...@@y...@@z passage before we call the original
  485. |prime_the_change_buffer|.
  486.  
  487. The procedure |compare_change_files| checks if the |change_buffers| of two
  488. |change_files| are equal. If so, those with the lower priority will be
  489. advanced.
  490.  
  491. @p procedure prime_the_change_buffer(i:ch_type);
  492. label continue, done, exit;
  493. var k:0..buf_size; {index into the buffers}
  494. begin change_limit[i]:=0; {this value will be used if the change file ends}
  495. @<Skip over comment lines in the change file; |return| if end of file@>;
  496. @<Skip to the next nonblank line; |return| if end of file@>;
  497. @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
  498. exit: end;
  499. @#
  500.  
  501. procedure pre_prime_the_change_buffer(i:ch_type);
  502. label continue, done, exit;
  503. begin change_limit[i]:=0; {this value will be used if the change file ends}
  504. loop@+ begin line:=ch_line[i];incr(line);ch_line[i]:=line;
  505.    input_ch(i);
  506.    if not ch_not_eof then
  507.       begin err_print ('! Change file ',i:1,' ended without @@z');
  508.       return end;
  509.    if limit < 2 then goto continue;
  510.    if ((buffer[0]="@@") and ((buffer[1]="z") or (buffer[1]="Z")))
  511.       then goto done;
  512. continue:end;
  513. done:
  514. prime_the_change_buffer(i);
  515. exit: end;
  516. @#
  517.  
  518. procedure compare_change_files;
  519. label restart;
  520. var i,j:0..ch_max;
  521. begin
  522. restart:
  523. for i:=1 to ch_max-1 do
  524.    for j:=i+1 to ch_max do
  525.    if not ch_dont_match(i,j) then
  526.       begin
  527.       print_nl(' WARNING: change_file ',i:1,' line ',ch_line[i]:4);
  528.       print_nl('      and change_file ',j:1,' line ',ch_line[j]:4,
  529.           ' refer to the same text !!!!');
  530.       print_nl(' Change_file ',j:1,' will be advanced.');new_line;
  531.       pre_prime_the_change_buffer(j);
  532.       mark_harmless; goto restart
  533.       end;
  534. end;
  535. @z
  536.  
  537. @x (129) we have to use an index for change-file
  538. @ While looking for a line that begins with \.{@@x} in the change file,
  539. we allow lines that begin with \.{@@}, as long as they don't begin with
  540. \.{@@y} or \.{@@z} (which would probably indicate that the change file is
  541. fouled up).
  542.  
  543. @<Skip over comment lines in the change file...@>=
  544. loop@+  begin incr(line);
  545.   if not input_ln(change_file) then return;
  546.   if limit<2 then goto continue;
  547.   if buffer[0]<>"@@" then goto continue;
  548.   if (buffer[1]>="X")and(buffer[1]<="Z") then
  549.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  550.   if buffer[1]="x" then goto done;
  551.   if (buffer[1]="y")or(buffer[1]="z") then
  552.     begin loc:=2; err_print('! Where is the matching @@x?');
  553. @.Where is the match...@>
  554.     end;
  555. continue: end;
  556. done:
  557. @y
  558. @ While looking for a line that begins with \.{@@x} in the change file
  559. indexed by |i|,
  560. we allow lines that begin with \.{@@}, as long as they don't begin with
  561. \.{@@y} or \.{@@z} (which would probably indicate that the change file is
  562. fouled up).
  563.  
  564. @<Skip over comment lines in the change file...@>=
  565. loop@+  begin line:=ch_line[i];incr(line);ch_line[i]:=line;
  566.   input_ch(i);
  567.   if not ch_not_eof then return;
  568.   if limit<2 then goto continue;
  569.   if buffer[0]<>"@@" then goto continue;
  570.   if (buffer[1]>="X")and(buffer[1]<="Z") then
  571.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  572.   if buffer[1]="x" then goto done;
  573.   if (buffer[1]="y")or(buffer[1]="z") then
  574.     begin loc:=2;
  575.       err_print('! Where is the matching @@x in change-file ',i:1,' ?');
  576. @.Where is the match...@>
  577.     end;
  578. continue: end;
  579. done:
  580. @z
  581.  
  582. @x (130) we have to use an index using change-file
  583. @ Here we are looking at lines following the \.{@@x}.
  584.  
  585. @<Skip to the next nonblank line...@>=
  586. repeat incr(line);
  587.   if not input_ln(change_file) then
  588.     begin err_print('! Change file ended after @@x');
  589. @.Change file ended...@>
  590.     return;
  591.     end;
  592. until limit>0;
  593. @y
  594. @ Here we are looking at lines following the \.{@@x}.
  595.  
  596. @<Skip to the next nonblank line...@>=
  597. repeat line:=ch_line[i];incr(line);ch_line[i]:=line;
  598.   input_ch(i);
  599.   if not ch_not_eof then
  600.     begin err_print('! Change file ',i:1,' ended after @@x');
  601. @.Change file ended...@>
  602.     return;
  603.     end;
  604. until limit>0;
  605. @z
  606.  
  607. @x (131) we have to specify the change-file by an index
  608. @ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
  609. begin change_limit:=limit;
  610. for k:=0 to limit do change_buffer[k]:=buffer[k];
  611. end
  612. @y
  613. @ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
  614. begin change_limit[i]:=limit;
  615. for k:=0 to limit do change_buffer[i,k]:=buffer[k];
  616. end
  617. @z
  618.  
  619. @x (132) we have to specify and work with the actual change-file
  620. @ The following procedure is used to see if the next change entry should
  621. go into effect; it is called only when |changing| is false.
  622. The idea is to test whether or not the current
  623. contents of |buffer| matches the current contents of |change_buffer|.
  624. If not, there's nothing more to do; but if so, a change is called for:
  625. All of the text down to the \.{@@y} is supposed to match. An error
  626. message is issued if any discrepancy is found. Then the procedure
  627. prepares to read the next line from |change_file|.
  628.  
  629. @p procedure check_change; {switches to |change_file| if the buffers match}
  630. label exit;
  631. var n:integer; {the number of discrepancies found}
  632. @!k:0..buf_size; {index into the buffers}
  633. begin if lines_dont_match then return;
  634. n:=0;
  635. loop@+  begin change_changing; {now it's |true|}
  636.   incr(line);
  637.   if not input_ln(change_file) then
  638.     begin err_print('! Change file ended before @@y');
  639. @.Change file ended...@>
  640.     change_limit:=0;  change_changing; {|false| again}
  641.     return;
  642.     end;
  643.   @<If the current line starts with \.{@@y},
  644.     report any discrepancies and |return|@>;
  645.   @<Move |buffer| and |limit|...@>;
  646.   change_changing; {now it's |false|}
  647.   incr(line);
  648.   if not input_ln(web_file) then
  649.     begin err_print('! WEB file ended during a change');
  650. @.WEB file ended...@>
  651.     input_has_ended:=true; return;
  652.     end;
  653.   if lines_dont_match then incr(n);
  654.   end;
  655. exit: end;
  656. @y
  657. @ The following procedure is used to see if the next change entry should
  658. go into effect; it is called only when |changing| is false.
  659. The idea is to test whether or not the current contents of |buffer|
  660. matches the current contents of one of the |change_buffers|.
  661. If not, there's nothing more to do; but if so, a change is called for:
  662. All of the text down to the \.{@@y} is supposed to match. An error
  663. message is issued if any discrepancy is found. Then the procedure
  664. prepares to read the next line from the current |change_file|.
  665.  
  666. Since we use several |change_file|s we first have to examine all
  667. |change_buffer|s. The index of the matching |change_file| is written to the
  668. variable |ch_act| by the function |lines_dont_match|.
  669. Each time one line of the current |change_file| is read into its
  670. |change_buffer| this one is compared with all the other |change_buffers|.
  671. In case of equality the latter |change_file| will be advanced by the
  672. function |pre_prime_the_change_buffer|.
  673.  
  674.  
  675. @p procedure check_change; {switches to |change_file| if the buffers match}
  676. label exit,done,continue;
  677. var n:integer; {index, the number of discrepancies found}
  678.     i:integer; {loop variable for processing the |change_files|}
  679.     temp:integer; {for temporary storage of the contents of |ch_act|}
  680. @!k:0..buf_size; {index into the buffers}
  681. begin
  682. for i:= 1 to ch_max do
  683.    if not lines_dont_match(i) then goto done;
  684.    return;{no change-file matches}
  685. done:{one change-file matches, the index has been written to ch-act}
  686.   n:=0;
  687. loop@+  begin change_changing; {now it's |true|}
  688.   line:=ch_line[ch_act];incr(line);ch_line[ch_act]:=line;
  689.   input_ch(ch_act);
  690.   if not ch_not_eof then
  691.     begin err_print('! Change file ',ch_act:1,' ended before @@y');
  692. @.Change file ended...@>
  693.     change_limit[ch_act]:=0;  change_changing; {|false| again}
  694.     return;
  695.     end;
  696.   @<If the current line starts with \.{@@y},
  697.     report any discrepancies and |return|@>;
  698.   @<Move |buffer| and |limit|...@>;
  699. {the actual change-buffer has to be compared with the other change-buffers,
  700. the index of the actual |change_file| is stored to the variable temp}
  701.     temp:=ch_act;
  702.     for i:=1 to ch_max do
  703.     begin
  704.     if i=ch_act then goto continue;
  705.     if not ch_dont_match(i,ch_act) then
  706.        begin
  707.        print_nl(' WARNING: change_file ',i:1,' line ',ch_line[i]:4);
  708.        print_nl('      and change_file ',ch_act:1,' line ',
  709.              ch_line[ch_act]:4,' refer to the same text !!!!');
  710.        print_nl(' Change_file ',i:1,' will be advanced.');new_line;
  711.        pre_prime_the_change_buffer(i);
  712.        end;
  713.     continue: end;
  714.   ch_act:=temp;i:=ch_act; {the actual index has to be reloaded}
  715.   change_changing; {now it's |false|}
  716.   incr(line);
  717.   if not input_ln(web_file) then
  718.     begin err_print('! WEB file ended during a change');
  719. @.WEB file ended...@>
  720.     input_has_ended:=true; return;
  721.     end;
  722.   if lines_dont_match(ch_act)  then incr(n);
  723.   end;
  724. exit: end;
  725. @z
  726.  
  727. @x (133) we specify the actual change-file in the error message
  728. @ @<If the current line starts with \.{@@y}...@>=
  729. if limit>1 then if buffer[0]="@@" then
  730.   begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  731.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  732.   if (buffer[1]="x")or(buffer[1]="z") then
  733.     begin loc:=2; err_print('! Where is the matching @@y?');
  734. @.Where is the match...@>
  735.     end
  736.   else if buffer[1]="y" then
  737.     begin if n>0 then
  738.       begin loc:=2; err_print('! Hmm... ',n:1,
  739.     ' of the preceding lines failed to match');
  740. @.Hmm... n of the preceding...@>
  741.       end;
  742.     return;
  743.     end;
  744.   end
  745. @y
  746. @ @<If the current line starts with \.{@@y}...@>=
  747. if limit>1 then if buffer[0]="@@" then
  748.   begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  749.     buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  750.   if (buffer[1]="x")or(buffer[1]="z") then
  751.     begin loc:=2;
  752.     err_print('! Where is the matching @@y in change_file ',ch_act:1,' ?');
  753. @.Where is the match...@>
  754.     end
  755.   else if buffer[1]="y" then
  756.     begin if n>0 then
  757.       begin loc:=2; err_print('! Hmm... ',n:1,
  758.     ' of the preceding lines failed to match in change_file ',ch_act:1);
  759. @.Hmm... n of the preceding...@>
  760.       end;
  761.     return;
  762.     end;
  763.   end
  764. @z
  765.  
  766. @x (134) to initialize we have to look at all change buffers
  767. @ @<Initialize the input system@>=
  768. open_input; line:=0; other_line:=0;@/
  769. changing:=true; prime_the_change_buffer; change_changing;@/
  770. limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
  771. @y
  772. @ @<Initialize the input system@>=
  773. open_input; line:=0; other_line:=0;@/
  774. for i:=1 to ch_max do ch_line[i]:=0;@/
  775. changing:=true;
  776. for i:=1 to ch_max do  prime_the_change_buffer(i);@/
  777. compare_change_files;change_changing;@/
  778. limit:=0; loc:=1; buffer[0]:=" "; input_has_ended:=false;
  779. @z
  780.  
  781. @x (135) we need a loop variable i
  782. @ The |get_line| procedure is called when |loc>limit|; it puts the next
  783. line of merged input into the buffer and updates the other variables
  784. appropriately. A space is placed at the right end of the line.
  785.  
  786. @p procedure get_line; {inputs the next line}
  787. label restart;
  788. begin restart: if changing then
  789.   @<Read from |change_file| and maybe turn off |changing|@>;
  790. if not changing then
  791.   begin @<Read from |web_file| and maybe turn on |changing|@>;
  792.   if changing then goto restart;
  793.   end;
  794. loc:=0; buffer[limit]:=" ";
  795. end;
  796. @y
  797. @ The |get_line| procedure is called when |loc>limit|; it puts the next
  798. line of merged input into the buffer and updates the other variables
  799. appropriately. A space is placed at the right end of the line.
  800.  
  801. @p procedure get_line; {inputs the next line}
  802. label restart;var i:integer;{ loop variable for processing the change files}
  803. ch_flag:boolean; {flag whether to use |check_change| or not}
  804. begin restart: if changing then
  805.   @<Read from |change_file| and maybe turn off |changing|@>;
  806. if not changing then
  807.   begin @<Read from |web_file| and maybe turn on |changing|@>;
  808.   if changing then goto restart;
  809.   end;
  810. loc:=0; buffer[limit]:=" ";
  811. end;
  812. @z
  813.  
  814. @x (136) we have to specify the actual change-file
  815. @ @<Read from |web_file|...@>=
  816. begin incr(line);
  817. if not input_ln(web_file) then input_has_ended:=true
  818. else if limit=change_limit then
  819.   if buffer[0]=change_buffer[0] then
  820.     if change_limit>0 then check_change;
  821. end
  822. @y
  823. @ @<Read from |web_file|...@>=
  824. begin incr(line);
  825. if not input_ln(web_file) then input_has_ended:=true
  826. else begin ch_flag:=false;
  827.      for i:=1 to ch_max do begin
  828.      if limit=change_limit[i] then
  829.   if buffer[0]=change_buffer[i,0] then
  830.     if change_limit[i]>0 then ch_flag:=true;  end;
  831.      if ch_flag then check_change;end;
  832. end
  833. @z
  834.  
  835. @x (137) we have to specify the change-file
  836. @ @<Read from |change_file|...@>=
  837. begin incr(line);
  838. if not input_ln(change_file) then
  839.   begin err_print('! Change file ended without @@z');
  840. @.Change file ended...@>
  841.   buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
  842.   end;
  843. if limit>1 then {check if the change has ended}
  844.   if buffer[0]="@@" then
  845.     begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  846.       buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  847.     if (buffer[1]="x")or(buffer[1]="y") then
  848.       begin loc:=2; err_print('! Where is the matching @@z?');
  849. @.Where is the match...@>
  850.       end
  851.     else if buffer[1]="z" then
  852.       begin prime_the_change_buffer; change_changing;
  853.       end;
  854.     end;
  855. end
  856. @y
  857. @ @<Read from |change_file|...@>=
  858. begin line:=ch_line[ch_act];incr(line);ch_line[ch_act]:=line;
  859. input_ch(ch_act);
  860. if not ch_not_eof then
  861.   begin err_print('! Change file ',ch_act:1,' ended without @@z');
  862. @.Change file ended...@>
  863.   buffer[0]:="@@"; buffer[1]:="z"; limit:=2;
  864.   end;
  865. if limit>1 then {check if the change has ended}
  866.   if buffer[0]="@@" then
  867.     begin if (buffer[1]>="X")and(buffer[1]<="Z") then
  868.       buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify}
  869.     if (buffer[1]="x")or(buffer[1]="y") then
  870.       begin loc:=2; err_print('! Where is the matching @@z in change_file ',
  871.                    ch_act:1,' ?');
  872. @.Where is the match...@>
  873.       end
  874.     else if buffer[1]="z" then
  875.       begin prime_the_change_buffer(ch_act);
  876.         compare_change_files; change_changing;
  877.       end;
  878.     end;
  879. end
  880. @z
  881.  
  882. @x (138) we have to specify the actual change-file
  883. @ At the end of the program, we will tell the user if the change file
  884. had a line that didn't match any relevant line in |web_file|.
  885.  
  886. @<Check that all changes have been read@>=
  887. if change_limit<>0 then {|changing| is false}
  888.   begin for loc:=0 to change_limit do buffer[loc]:=change_buffer[loc];
  889.   limit:=change_limit; changing:=true; line:=other_line; loc:=change_limit;
  890.   err_print('! Change file entry did not match');
  891. @y
  892. @ At the end of the program, we will tell the user if one of the
  893. |change_file|s had a line that didn't match any relevant line in |web_file|.
  894.  
  895. @<Check that all changes have been read@>=
  896. for i:=1 to ch_max do begin
  897. if change_limit[i]<>0 then {|changing| is false}
  898.   begin for loc:=0 to change_limit[i] do buffer[loc]:=change_buffer[i,loc];
  899.   limit:=change_limit[i]; changing:=true; line:=ch_line[i];
  900.   loc:=change_limit[i];
  901.   ch_act:=i;
  902.   err_print('! Change_file ',i:1,' entry did not match');
  903. @.Change file entry did not match@>
  904.   end
  905. @z
  906. @x term_in (input) must not be redefined. (179)
  907. @!term_in:text_file; {the user's terminal as an input file}
  908. @y
  909. @z
  910.